<?php
namespace App\GameService\games;

use App\Common\GameCommon;
use App\Utility\BaseEnum;
use App\Common\AuthCommon;
use Swoole\Timer;
use App\Utility\RedisTools;
use App\Enum\MahjEnum;
use App\Common\BaseComm;
use App\Enum\CardEnum;
use App\Enum\SysEnum;
use App\Common\PlayCommon;

/**
 * 二八杠游戏
 *
 * @author Administrator
 */
class PlayTwoEight extends BasePlay
{
    // 事件
    public function __construct()
    {
        //配置时间:时间问题
        $this-> gtimeConfig = array(
            "quangzhuang_long" => 8,    //抢庄时长
            "waitbet_long" => 5,        //等待下注
            "playbet_long" => 5,        //下注时长
            "playsaizi_long" => 2,      //下注完成，等待摇甩子时长
            "waitcard_long" => 1,       //摇甩子完成，等待发牌时长
            "waitdeal_long" => 5,       //摇甩子完成，等待结算时长
            "nextgame_long" => 15,       //进入第二局默认等待时长
        );
    }

    /**
     * TODO 看牌提前发三张牌
     *
     * @param array $playFds
     * @param array $useCards
     */
    protected function PushFrontThreeCards($playFds, $useCards = FALSE)
    {
        if (! $useCards || empty($useCards))
            return false; // 推送每个位置的牌值；
        foreach ($useCards as $onecards) {
            $seatid = isset($onecards["seatNo"]) ? intval($onecards["seatNo"]) : - 1; // 座位号
            $cards = isset($onecards["cards"]) ? $onecards["cards"] : FALSE;
            if (! empty($cards) && sizeof($cards) > 3 && $seatid >= 0 && isset($playFds[$seatid])) {
                $oneCard = [
                    "seatNo" => $seatid,
                    "cards" => array_slice($cards, 0, 3),
                    "step" => 0
                ]; // 只取前三张牌
                AuthCommon::pushFrontWeb($playFds[$seatid], json_encode([
                    'm' => BaseEnum::XYM_0X17,
                    "data" => $oneCard
                ])); // 推送相同内容
            }
        }
        return true;
    }

    /**
     * TODO 二八游戏入口；
     * TODO 必须实现这个方法；
     * TODO 游戏开始逻辑
     */
    public function asycStartGame($gameNo, $gameRId = FALSE, $fjInfo = FALSE, &$commonLoopTimer = 0)
    {
        $gameId = isset($fjInfo['gameId']) ? intval($fjInfo['gameId']) : 0;
        $gameAllData = BaseComm::distMemoryRooms($gameNo, FALSE); // 内存数据；
        $fjSta = isset($gameAllData['fjStatus']) ? intval($gameAllData['fjStatus']) : 0;
        $gamePlays = isset($gameAllData['lists']) ? $gameAllData['lists'] : FALSE; // 玩家列表；
        $playFds = ! empty($gamePlays) ? array_column($gamePlays, "fd", "seatNo") : false; // 座位列表
        BaseEnum::Logger('取内存数据:Games-(PlayTwoEight)-[asycStartGame]--->>>' . json_encode($gameAllData), 'user-login');

        // 刚开始就给玩家生成牌的数据；
        $useCards = GameCommon::createCardList($gameId, $gameNo, false); // 启动发牌 ： XYM_0X19；
        BaseEnum::Logger("游戏开始生成牌:Games-(PlayTwoEight)-{$gameId} 再计算计算谁是[{$gameNo}]庄主---->>>" . json_encode($useCards), 'user-login'); // 所有玩家FD
        $Parms = [
            "gameNo" => $gameNo,
            "clientIds" => $playFds,
            "status" => $fjSta
        ];
        
        //TODO 开启机器人自动抢庄
        $this->startRobotDoQiangz($gameId, $gameNo);

        // 抢庄====倒计时10秒钟
        $qzLong = $this->getConfigTimeLong('quangzhuang_long') ?? 5;
        $this->asycTimeStart($qzLong, $gameNo, $Parms, function () use ($gameNo, $gameRId, $gamePlays, $playFds, $fjInfo, $useCards) {
            $error = '';
            $notQzUser = FALSE;
            if (! $fjInfo) {
                $fjInfo = BaseComm::GameRoomDetail($gameRId);
            }
            $gameId = isset($fjInfo['gameId']) ? intval($fjInfo['gameId']) : 0;
            $gameData = BaseComm::distMemoryRooms($gameNo, FALSE); // 内存数据；
            $firstStamp = isset($gameData['create']) ? intval($gameData['create']) : 0;
            $gamePlays = isset($gameData['lists']) ? $gameData['lists'] : FALSE; // 玩家列表；
            $playFds = ! empty($gamePlays) ? array_column($gamePlays, "fd", "seatNo") : false; // 座位列表
            BaseEnum::Logger("选庄家定时器：Games-(PlayTwoEight)-==========>  ，计算谁是[{$gameNo}]坐庄---->>>" . json_encode($playFds), 'user-login'); // 所有玩家FD
            // 计算谁是庄家；
            $zjUser = GameCommon::countRoomMaimZhuang($gameNo, $gamePlays, $notQzUser);
            $zjUid = isset($zjUser['uid']) ? intval($zjUser['uid']) : 0; // 抢到倍数大于0
            $zjSeatid = isset($zjUser['seatNo']) ? intval($zjUser['seatNo']) : - 1; // 抢到倍数大于0
            $zjMuts = isset($zjUser['mutdotts']) ? intval($zjUser['mutdotts']) : 0; // 抢到倍数大于0
            $zjPoints = isset($zjUser['points']) ? floatval($zjUser['points']) : 0; // 抢到倍数大于0
            BaseEnum::Logger("抢庄统计：Games-(PlayTwoEight)-哪些用户没有抢庄 ===> " . json_encode($notQzUser), 'user-login');
            if (! empty($notQzUser)) { // 显示不抢庄的玩家
                foreach ($notQzUser as $onenotxj) {
                    AuthCommon::pushFrontWeb($playFds, json_encode([
                        'm' => BaseEnum::XYM_0X18,
                        "data" => $onenotxj
                    ])); // 推送不抢庄
                }
            }
            if (isset($zjUser) && $zjUid > 0 && isset($zjUser['fd']) && intval($zjUser['fd']) > 0 && ! empty($playFds) && $zjMuts > 0 && $zjPoints > 0) { // 抢zhuang 成功
                $totalNum = sizeof($playFds); // 通知给所有玩家，谁是庄家；
                $showList = array(
                    "seatNo" => $zjSeatid,
                    "mutdotts" => $zjMuts,
                    "max_mutle" => 1,
                    "nums" => $totalNum
                ); // 最大下注倍数

                // 抢zhuang 成功；
                // 计算每个用户最大倍数实际下注金额是底注的多少倍 ；不超过最大值（二八杠要考虑是否超过庄家的最大倍数）；
                $maxMutl = isset($fjInfo["maxMultiple"]) ? intval($fjInfo["maxMultiple"]) : 0; // 房间最高倍 // 按人数来,不超过最大位置；
                $betmaxMutl = ($zjMuts > 5) ? intval($zjMuts / 3) : 3;
                if ($betmaxMutl > $maxMutl && $maxMutl > 0) {
                    $betmaxMutl = $maxMutl;
                } else if ($betmaxMutl < 1) {
                    $betmaxMutl = 1;
                }
                $showList["max_mutle"] = $betmaxMutl;
                $savedata = array(
                    "ptype" => 1,
                    "userId" => $zjUid,
                    "data" => $zjMuts
                );
                $maxut = GameCommon::updateRoomsPInfo($gameNo, $savedata, $gamePlays, $error); // 游戏记录写DB数据; ======> 记录玩家中谁是庄家及倍数；

                //TODO  抢成功才会到下一步
                if (isset($maxut) && $maxut === true) {
                    RedisTools::Pool_hDel(SysEnum::ONLINE_BDATA_KEY,$gameNo);    //抢庄数据处理清掉缓存里值   
                    
                    //TODO 推送到所有FD，庄家的座位号；[XYM_0X19 --抢庄成功 ]
                    BaseEnum::Logger("抢庄成功：Games-(PlayTwoEight)-恭喜用户 UID={$zjUid} 抢庄(QZ：{$zjMuts}倍)成功---->>> SUCCESS !!!!" . $gameNo, 'user-login');                    
                    
                    //TODO 获取起始时间间隔
                    $countStamp = BaseEnum::getNowTimeLong($firstStamp);
                    BaseComm::gameLogger($gameNo,"开局第{$countStamp}秒  , 定庄完成, {$zjSeatid} 号位为庄家 ({$zjMuts}倍),最高下注倍数：" . $showList["max_mutle"]);            //TODO 对局开始日志
                    $pushdata = [  'm' => BaseEnum::XYM_0X1A,  "data" => $showList ];
                    AuthCommon::pushFrontWeb($playFds, json_encode($pushdata)); 
                    
                    // 标识为庄主
                    $zjUserInfo = array(
                        "uid" => $zjUid,
                        "seatNo" => $zjSeatid,
                        "bet_mutle" => $zjMuts,
                        "main_flag" => 1
                    ); 

                    /* TODO 启动下一个定时器 ===> 闲家选倍数 */
                    $this->StartUserBetMuts($gameId, $gameRId, $gameNo, $gamePlays, $zjUid, $zjUserInfo);
                } else {
                    // 异常失败
                    AuthCommon::pushFrontWeb($playFds, json_encode([
                        'm' => $maxut,
                        "data" => null,
                        "msg" => $error
                    ])); 
                }
            } else {
                BaseEnum::Logger('抢庄失败：PlayTwoEight-(asycStartGame)-执行定时器计算抢庄失败 ---->>>Fail !!!!' . $gameNo, 'user-login');
            }
        });
    
         
    }

    
    /**
     * TODO 游戏玩家下注实现(下注把倍数存入内存中处理)
     * @param string $gameNo
     * @param int $roomId
     * @param boolean $params
     */
    public function asycPlayBets($roomId,$gameNo,$params = FALSE,& $errorNo = -1){
        $userId = isset($params['loginUid']) ? intval($params['loginUid']) : 0 ;
        $qmutil = isset($params['qmutil']) ? intval($params['qmutil']) : 1 ; //下注倍数
        if (!$roomId || empty($gameNo) || empty($params) || $userId <= 0) {
            return  FALSE;
        }
        $gameDetail = BaseComm::distMemoryRooms($gameNo, FALSE);        // 内存数据；
        $fjStatus = isset($gameDetail['fjStatus']) ? intval($gameDetail['fjStatus']) : 0;
        $users = isset($gameDetail['lists']) ? $gameDetail['lists'] : FALSE;
        
        
        //庄家ID
        $zjUid = 0 ;
        if(!empty($users)){
            foreach ( $users as $oneuu ){
                if(isset($oneuu['uid']) && intval($oneuu['uid'])>0 && isset($oneuu['mainflag']) && intval($oneuu['mainflag'])==1){
                    $zjUid = intval($oneuu['uid']) ; 
                    break;
                }
            }
        }
        
        
        BaseEnum::Logger("游戏数据 : (asycPlayBets - [{$gameNo}])==>" . json_encode($gameDetail), 'user-login');
        if (!$gameDetail || !$users) {
            $errorNo = BaseEnum::MSG_0XFA;
            return FALSE;
        }
        
        //非下注状态；
        if(!$fjStatus || $fjStatus !== 1 ){
            $errorNo = BaseEnum::MSG_0XFD;
            return FALSE;
        }
        
        //庄家不能下注
        if ($zjUid == $userId) {
            $errorNo = BaseEnum::MSG_0XF2;
            return FALSE;;
        }
        
        
        
        $userSeat = array_column($users, "seatNo","uid");
        $userFds = array_column($users, "fd","seatNo");
        
        //TODO 实现下注；写入数据进到内存中（结构:lists为玩家信息，bets 下注列表）
        $userBets  = isset($gameDetail['bets']) ? $gameDetail['bets'] :  [];
        $pushObj = FALSE;
        foreach ($userSeat as $uuid => $seatIndex){
            if ($uuid && intval($uuid) === $userId) {
                $userBets[$seatIndex] = $qmutil ; //下注倍数存起放在内存中
                $pushObj = [ "seatNo" => $seatIndex, "mutdotts" => $qmutil, "p_fd" => $userId];
                break;
            }
        }
        
        //下注数据存起来
        $gameDetail['bets'] = $userBets;
        BaseComm::distMemoryRooms($gameNo,TRUE,$gameDetail);
        
        // 下注信息
        $pushData = array( "m" => BaseEnum::XYM_0X19, "data" => $pushObj ); 
        BaseEnum::Logger("玩家下注成功 : (asycPlayBets - [{$gameNo}])==>" . json_encode($pushObj), 'user-login');
        AuthCommon::pushFrontWeb($userFds, json_encode($pushData)); // 通知给所有玩家，谁在抢庄；
        
        //TODO  下注人员都满员启动提前结束定时器
        if(!empty($userBets) && sizeof($userBets) > 0 &&  sizeof($userBets) + 1 >= sizeof($userSeat)){
            PlayHelp::getInstance()->setCommTimerStop($gameNo,true);    //停掉定时器
            BaseEnum::Logger("所有玩家下注完成：========：(asycPlayBets) -- [ STOP ]-RoomId=". $gameNo ."  =======================设置为1,中断操作========>>>>>>", 'user-login');
        }
        
        return TRUE;
    }
    
    /**
     * TODO 进入下一步处理事件
     */
    public function doNextPlayBets($loginFd, $gameNo, $fjDetail = FALSE, $getUix = FALSE)
    {
        //TODO 动画处理完触发；只能调用一次；触发动画倒计时；
        $gameId = isset($fjDetail['gameId']) ? intval($fjDetail['gameId']) : 0;
        $gameRid = isset($fjDetail['grid']) ? intval($fjDetail['grid']) : 0;
        $fjSta = isset($fjDetail['fjStatus']) ? intval($fjDetail['fjStatus']) : 0;
        $gamePlays = isset($fjDetail['lists']) ? $fjDetail['lists'] : FALSE; // 玩家列表；
        $playerFds = ! empty($gamePlays) ? array_column($gamePlays, "fd", "seatNo") : false; // 座位列表
        $Parms = [
            "gameNo" => $gameNo,
            "clientIds" => $playerFds,
            "status" => $fjSta
        ];
        $zjUserInfo = FALSE;
        if (! empty($gamePlays) && sizeof($gamePlays) >= 2) { // 查找庄家信息；
            foreach ($gamePlays as $oneuser) {
                if (isset($oneuser["uid"]) && intval($oneuser["uid"]) > 0 && isset($oneuser["mainflag"]) && intval($oneuser["mainflag"]) === 1) { // 庄家 mainflag = 1
                    $zjUserInfo = array(
                        "uid" => $oneuser["uid"],
                        "seatNo" => @$oneuser["seatNo"],
                        "bet_mutle" => isset($oneuser['mutdotts']) ? intval($oneuser['mutdotts']) : 0,
                        "main_flag" => 1 // 标识为庄主;
                    );
                    break;
                }
            }
        }
        
        // 标识为庄主;
        if (! $zjUserInfo || ! isset($zjUserInfo['uid']) || intval($zjUserInfo['uid']) <= 0){
            return FALSE;
        }
        //TODO 开启机器人自动下注功能
        $this->startRobotDoBets($gameId,$gameRid, $gameNo);
        $Long = $this->getConfigTimeLong('playbet_long') ?? 5;
        $this->asycTimeStart($Long, $gameNo, $Parms, function () use ($gameNo, $gamePlays, $playerFds, $fjDetail, $zjUserInfo) {
            $gameId = isset($fjDetail["gameId"]) ? intval($fjDetail["gameId"]) : 0;     // 游戏ID值；
            $gameRId = isset($fjDetail["grid"]) ? intval($fjDetail["grid"]) : 0;        // 房间ID值；
            $zjUserid = isset($zjUserInfo["uid"]) ? intval($zjUserInfo["uid"]) : 0;
            $firstStamp = isset($fjDetail["create"]) ? intval($fjDetail["create"]) : 0; 
            $useCards = isset($fjDetail["frontCards"]) ? $fjDetail["frontCards"] : FALSE; // 提前发的牌数据
            
            //TODO 获取起始时间间隔
            $countStamp = BaseEnum::getNowTimeLong($firstStamp);
            
            //房间内的数据
            $gameDetail = BaseComm::distMemoryRooms($gameNo,FALSE);
            $userBets  = isset($gameDetail['bets']) ? $gameDetail['bets'] :  [];            //下注的数据内容
            $gamePlays = isset($gameDetail['lists']) ? $gameDetail['lists'] : FALSE;        // 玩家列表；
            $allSeat = array_column($gamePlays, 'seatNo',"uid");
            
            //对局日志
            $betLoggs=["开局第{$countStamp}秒  , 下注"];
            
            
            
            
            /**
             * TODO 下注==== 玩家下注TO DB  
             * TODO 实际步骤：
             * 1、检查所有玩家是否有下注和下注倍数据
             * 2、把没有下注完的默认下1倍；并推给所有玩家
             */
            BaseEnum::Logger("执行定时器计算：PlayTwoEight-(asycStartUserBetMuts)-=====[投注倍数]  ，去列队中给[{$gameNo}]所有玩家下单--（DB）-->>>" . json_encode($playerFds), 'user-login');
            BaseEnum::Logger('取内存数据：PlayTwoEight-(BaseDB::gameRoomPlayerData)-[asycStartUserBetMuts]--->>>' . json_encode($gameDetail), 'user-login');
            
            
            $userBetIds = [];
            $noticeUsers = [];            
            //TODO 计算未下注的用户信息
            foreach ($allSeat as $uuid => $setid2){
                if (isset($uuid) && intval($uuid)>0 && intval($uuid) === $zjUserid) continue;
                $usrbetMul = isset($userBets[$setid2]) ? intval($userBets[$setid2]) : 0;        //下注倍数
                if($uuid > 0 && $usrbetMul <= 0 ){        
                    $usrbetMul = 1;
                    $userBets[$setid2] = 1 ;
                    $noticeUsers[$uuid] = array("uid" =>$uuid,"seatNo" =>$setid2, "bet_mutle" => 1);        //未下注
                }
                $betLoggs[] = "{$setid2} 号位  下注{$usrbetMul} 倍";
                $userBetIds[] = $uuid;              //下注列表
            }
            
            
            //TODO 修改内存中游戏状态
            foreach ($gamePlays as $setiid => &$oneplay) {
                if (isset($userBets[$setiid]) && intval($userBets[$setiid]) > 0) {
                    $oneplay["mutdotts"] = intval($userBets[$setiid]);       // 修改下注倍数
                    $oneplay["pstatus"] = CardEnum::GAME_STEP_2;             // 修改下注成功；
                }
            }
            $gameDetail['bets'] = $userBets;
            $gameDetail['fjStatus'] = CardEnum::GAME_STEP_2;                // 下注成功
            $gameDetail['lists'] = $gamePlays;
            BaseComm::distMemoryRooms($gameNo,TRUE,$gameDetail);            //TODO 存储游戏数据
            BaseEnum::wirteRedisData(SysEnum::ONLINE_ROOMS_LIST_KEY . $gameNo, json_encode($gameDetail));
            
            
            
            //TODO 所有玩家订单入库处理： (闲家下注)
            $savedata = array("ptype"=> 2 ,"userId" => implode(',', $userBetIds) ,"data"=> $userBets);
            $dbFlag = GameCommon::updateRoomsPInfo($gameNo,$savedata,$gamePlays,$error);  //下注成功；
            
            if ($dbFlag !== FALSE) {
                BaseEnum::Logger("闲家下注失败：(下注) ->>> ERROR，  未下注玩家：" . json_encode($noticeUsers), 'user-login');
                $userBetInfo[$zjUserid] = $zjUserInfo;      // 合并庄家的ID
            }
             
            // TODO 单个数据推到前端；
            $allFds = array_column($gamePlays, "fd","seatNo");
            BaseComm::gameLogger($gameNo,$betLoggs);       //下注日志   
            if (! empty($noticeUsers)) {
                $noticeUsers = array_column($noticeUsers, null, "seatNo");  // 通知给所有玩家，下注倍数；
                foreach ($noticeUsers as $siteid => $oneline) {
                    $oneInfo = [ "seatNo" => $siteid, "mutdotts" => isset($oneline['bet_mutle']) ? intval($oneline['bet_mutle']) : 1, "self" => 1];
                    $pushdata = ['m' => BaseEnum::XYM_0X19, "data" => $oneInfo];
                    AuthCommon::pushFrontWeb($allFds, json_encode($pushdata));   // 推送未下注玩家默认1倍
                }
            }
            
            
            // TODO 启动摇色子[XYM_0X30]
            $Long = $this->getConfigTimeLong('playsaizi_long') ?? 2;
            Timer::after($Long * 1000, function() use($gameId, $gameRId, $gameNo,$allFds){
                
                BaseEnum::Logger("启动摇色子：(摇色子) ->>> Start，  未下注玩家：" . json_encode([$gameId, $gameRId, $gameNo]), 'user-login');
                $this->StartCards($gameId, $gameRId, $gameNo, $allFds);  
            });
            
            return FALSE;
        });
    }

    /**
     * TODO 恢复游戏数据；
     * 处理以下几张情况；
     * 1、抢庄中---谁庄过庄（标识出来），
     * 2、下注中(谁是庄家标识出来，)
     * 3、结算中(庄家，下注倍数，当前玩家牌值内容)
     *
     * 附状态值：0 => '匹配', 1 => '抢庄', 2 => '下注', 3 => '发牌', 4 => '拼牌', 5 => '结算',
     *
     * @param int $gameStatus
     * @param array $gamePlays
     */
    public static function reBackData($loginUid, $gameStatus, $gameNo = FALSE, $gamePlays = FALSE)
    {
        if (! $loginUid || ! $gamePlays || ! $gameNo)
            return false;
        $redisobj = new RedisTools();
        $redisobj->select(0);
        $palyUser = $gamePlays;
        foreach ($palyUser as & $oneus) {
            unset($oneus['fd']);
            unset($oneus['agentId']);
            unset($oneus['betmoney']);
        }
        //匹配成功 开始抢庄 ===> 谁有抢庄
        if ($gameStatus === CardEnum::GAME_STEP_0) {
            $rediskey = SysEnum::ONLINE_BDATA_KEY . "_MUT"; // 查询下注数据；
            $betdata = $redisobj->hGet($rediskey, $gameNo);
            $betArray = ! empty($betdata) ? json_decode($betdata, TRUE) : FALSE;
            foreach ($palyUser as $sid => $oneuser) {
                $palyUser[$sid]['pstatus'] = '玩家抢庄';
            }
        } 
        // 抢庄完成 开始下注 ===> 标出谁是庄家,闲家下注倍数；
        else if ($gameStatus === CardEnum::GAME_STEP_1) {
            $rediskey = SysEnum::ONLINE_BDATA_KEY . "_MUT"; // 查询下注数据；
            $betdata = $redisobj->hGet($rediskey, $gameNo);
            $betArray = ! empty($betdata) ? json_decode($betdata, TRUE) : FALSE;
            foreach ($palyUser as $sid => $oneuser) {
                $palyUser[$sid]['pstatus'] = '闲家下注';
                if (isset($oneuser['mainflag']) && intval($oneuser['mainflag']) == 1) { // 定庄：uid,
                    $palyUser[$sid]['text'] = '庄';
                    $palyUser[$sid]['pstatus'] = '等待闲家下注';
                } else {
                    $palyUser[$sid]['text'] = '闲';
                    $palyUser[$sid]['mutdotts'] = isset($betArray[$sid]) ? intval($betArray[$sid]) : 0; // 闲家下注倍数
                    $palyUser[$sid]['max_mutle'] = 15; // 最大下注倍数；
                }
            }
        } 
        // 下注完成 开始发牌 拼牌 ===> 开始发牌（显示下注倍数）
        else if (in_array($gameStatus, [ CardEnum::GAME_STEP_2, CardEnum::GAME_STEP_3], true)) {
            $cards = BaseComm::distMemoryCards($gameNo,FALSE); // 全局变量获取手牌数据（座位号seatNo=> [11,23,233]）
            foreach ($palyUser as $sid => $oneuser) {
                $palyUser[$sid]['pstatus'] = ($gameStatus == 3) ? "拼牌中" : '发牌中';
                $palyUser[$sid]['ifcards'] = 1; // 已发牌
                $palyUser[$sid]['cards'] = false; // 已发牌
                if (isset($oneuser['uid']) && intval($oneuser['uid']) == $loginUid) { // 显示发给我的手牌；
                    $palyUser[$sid]['cards'] = isset($cards[$sid]) ? $cards[$sid] : [];
                }
            }
        } 
        // 拼牌完成 摊牌 ===> 结算数据（显示玩家最后的结算数据）
        else if (in_array($gameStatus, [CardEnum::GAME_STEP_4,CardEnum::GAME_STEP_5],true)) {
            $cards = BaseComm::distMemoryCards($gameNo,FALSE); // 全局变量获取手牌数据（座位号seatNo=> [11,23,233]）
            foreach ($palyUser as $sid => $oneuser) {
                $palyUser[$sid]['pstatus'] = "结算中";
                $palyUser[$sid]['ifcards'] = 1; // 已发牌
                $palyUser[$sid]['cards'] = isset($cards[$sid]) ? $cards[$sid] : [];

                // 显示结算所的金额数据；
                $palyUser[$sid]['lastmoney'] = 0.01;
            }
        }
        return $palyUser;
    }

    
    /**
     * TODO 结算二八杠的逻辑处理
     *
     * 牌型：天王 > 九宝 > 八宝 > 一宝 >二八杠 > 九点半
     * 对于 点牌；
     *
     *
     * @param boolean $zjdata
     * @param boolean $xjdata
     * @param boolean $fjdata
     * @param boolean $returndata
     */
    public static function dealRoomGameTWOEBAR($zjdata = false, $xjdata = false, $fjdata = false, &$returndata = false)
    {
        $zjNiuLevel = 0;    // TODO 比牌操作；庄家跟每个闲家一一对比；
        $zjTotalMoney = 0;  // TODO 庄家的总钱
        $gameUtype = isset($fjdata["gpKey"]) ? $fjdata["gpKey"] : ""; // 游戏类型
        $fjmoney = isset($fjdata["everyPoints"]) ? floatval($fjdata["everyPoints"]) : 0; // 房间低注金额
        $fjmaxPeop = isset($fjdata["maxPlayer"]) ? intval($fjdata["maxPlayer"]) : 0; // 房间低注金额
        $zjcode = isset($zjdata['cards']) ? $zjdata['cards'] : false; // 庄家的牌数据；
        $zjMutle = isset($zjdata['mainMutle']) ? intval($zjdata['mainMutle']) : 1; // 庄家倍数；
        $zjSeatNo = isset($zjdata['seatNo']) ? intval($zjdata['seatNo']) : 0; // 庄家座位号；
        $zjUserId = isset($zjdata['userId']) ? intval($zjdata['userId']) : 0; // 庄家UID；
        $allCards = [
            $zjSeatNo => $zjcode
        ];
        $moneyArr = [
            $zjSeatNo => array()
        ];
        $zjRes = MahjEnum::dealTwoEightLevel($zjcode, $zjNiuLevel);

        if (! empty($zjdata) && ! empty($xjdata) && sizeof($xjdata) > 0 && ! empty($zjRes) && $fjmoney > 0 && $zjSeatNo >= 0) {
            $zjCardmutl = isset($zjRes['mutl_value']) ? $zjRes['mutl_value'] : 1; // 牌型倍数
            $zjFees = []; //所有庄家下注
            $zjRes['cards'] = $zjcode;
            // echo '庄-><br>'.print_r($zjRes,true);
            foreach ($xjdata as $xjinfo) {
                $xjLevel = - 1;     // 闲家牛等级
                $xjMoney = 0;       // 闲家赢的钱
                $xjWater = 0;       // 最后计算抽水的钱；
                $xjLastMoney = 0 ;  //玩家最后盈亏的钱

                $xjBets = isset($xjinfo['mainMutle']) ? intval($xjinfo['mainMutle']) : 1; // 闲家下注倍数；
                $xianjCard = isset($xjinfo['cards']) ? $xjinfo['cards'] : false;
                $xjSeatNo = isset($xjinfo['seatNo']) ? intval($xjinfo['seatNo']) : 0; // 庄家座位号；
                $xjUserId = isset($xjinfo['userId']) ? intval($xjinfo['userId']) : 0; // 庄家座位号；
                $allCards[$xjSeatNo] = $xianjCard;

                $xjRes = MahjEnum::dealTwoEightLevel($xianjCard, $xjLevel);
                $lastWin = 1; // 通过计算谁家赢：1-庄赢，2-闲赢,
                if (! empty($xianjCard) && ! empty($xjRes) && $xjLevel >= 0 && $xjSeatNo >= 0 && isset($xjUserId) && intval($xjUserId) >0) {
                    $xjCardMut = isset($xjRes['mutl_value']) ? $xjRes['mutl_value'] : 1; // 牌型倍数
                    $xjRes['cards'] = $xianjCard;
                    // echo "闲->{$xjSeatNo}<br>".print_r($xjRes,true);
                    // 比大小：第一步，比KEY值；
                    if ($zjNiuLevel > $xjLevel) { // 牛相比； 庄赢；
                        $lastWin = 1;
                    } else if ($zjNiuLevel < $xjLevel) { // 牛相比； 闲赢；
                        $lastWin = 2;
                    } else if ($zjNiuLevel == $xjLevel) { // 牛相同，比其它； 点数；
                                                          // 牛牛相同时：比较大小；闲赢还是庄赢；
                        $lastWin = self::gatherWhoWin($zjRes, $xjRes, $xjLevel);
                    }
                    // 计算输赢的钱数据；
                    $zjMoney = self::countWinMoney($lastWin, $fjmoney, $zjMutle, $zjCardmutl, $xjBets, $xjCardMut, $xjMoney);
                    $xjLastMoney = $xjMoney;
                    if($xjMoney > 0){
                        $xjWater = bcmul($xjMoney, BaseComm::getSetWaterDott(), 2); // 最后计算抽水的钱
                        $xjLastMoney = bcsub($xjMoney, $xjWater,2);
                    }
                    $zjTotalMoney = bcadd($zjTotalMoney, $zjMoney, 2);
                    $zjFees[] = abs($zjMoney);
                    // var_dump("庄家赢的钱： ".$zjMoney . " 总共： ". $zjTotalMoney);
                    $xjBetMoney = abs($xjMoney); // 玩家的下注的钱:玩家赢的钱，和输的钱：只有正数；
                    $moneyArr[$xjSeatNo] = array(
                        "uid" => $xjUserId,
                        "seatNo" => $xjSeatNo,
                        "money" => floatval($xjMoney),
                        "cellScore" =>  $xjBetMoney,                       // 有效下注（取所有位置的下注总和）下多少分
                        "allBetScore" => $xjBetMoney,                      // 所有位下注总分
                        "lastPoints"=>  floatval($xjLastMoney),            // 实际输赢（除去水钱）
                        "level" => $xjLevel,
                        "water" => $xjWater,
                        "card_mutle" => $xjCardMut,
                        "zj" => '闲',
                        "cards" => $xianjCard
                    );
                }
            }

            //TODO  闲家信息没有问题；
            if (! empty($moneyArr) && sizeof($moneyArr) >= 1) {
                $zjWater = 0;
                $allBetZj = !empty($zjFees) ? array_sum($zjFees) : 0 ;
                $zjLastMoney = $zjTotalMoney;
                if (floatval($zjTotalMoney) > 0) {
                    $zjWater = bcmul($zjTotalMoney, BaseComm::getSetWaterDott(), 2); // 最后计算抽水的钱
                    $zjLastMoney = bcsub($zjTotalMoney, $zjWater , 2);
                }
                $moneyArr[$zjSeatNo] = array(
                    "uid" => $zjUserId,
                    "seatNo" => $zjSeatNo,
                    "money" => floatval($zjTotalMoney),
                    "cellScore" => abs($allBetZj),
                    "allBetScore" => abs($allBetZj),
                    "lastPoints"=>  floatval($zjLastMoney),            // 实际输赢（除去水钱）
                    "level" => $zjNiuLevel,
                    "water" => $zjWater,
                    "card_mutle" => $zjCardmutl,
                    "bet" => '庄',
                    "cards" => $zjcode
                );
                //TODO  手牌信息组合；包含信息：总共多少位置，每个玩家的手牌，庄家位置信息；
                $cardValue = BaseComm::conectUserCardsValue($gameUtype, $fjmaxPeop, $allCards, $zjSeatNo);
                $returndata["cardvalues"] = ! empty($cardValue) ? $cardValue : ""; // 手牌组合数据
            }
        }
        return $moneyArr;
    }

    /**
     * TODO 比较大小；闲赢还是庄赢；
     * @return boolean
     */
    private static function gatherWhoWin($zjRes, $xjInfo = false, $zjNiuLevel = false)
    {
        $lastWin = 1;
        // 比谁的最大点数大；
        if (isset($zjRes["maxCardKey"]) && intval($zjRes["maxCardKey"]) > 0 && isset($xjInfo["maxCardKey"]) && intval($xjInfo["maxCardKey"])) {
            // 当两个点数相同也是庄家赢
            if (intval($zjRes["maxCardKey"]) < intval($xjInfo["maxCardKey"])) { // 闲家赢
                $lastWin = 2;
            }
        } else { // 比牌值
                 // 比相同点数时的数据：
            $zjCard = isset($zjRes["cards"]) ? $zjRes["cards"] : FALSE;
            $xjCard = isset($xjInfo["cards"]) ? $xjInfo["cards"] : FALSE;

            // 完全一样的牌都是庄家赢；
            if (! empty($zjCard) && ! empty($xjCard) && max($zjCard) < max($xjCard)) { // 取最的大牌值；
                $lastWin = 2;
            }
        }
        return $lastWin;
    }

    /**
     * TODO 计算庄输赢数据（不考虑赢家抽水的情况）
     */
    private static function countWinMoney($mainwin = 1, $fjmoney = 0, $zjMutle = 0, $zjCardmutl = 1, $xjBets = 1, $xjCardMut = 1, & $xianMoney = 0)
    {
        // 庄赢 ：庄家胜利所赢 ＝ 闲家下注倍数*房间底注 －抽水 | 闲家失败所输 ＝ 闲家下注倍数*房间底注
        $mainMoney = 0;
        if (intval($mainwin) === 1) {
            $tempMon = $fjmoney * $xjBets; // * $zjMutle * $xjBets ; //A*M*X*Y
            $mainMoney = bcsub($tempMon, 0, 2); // 庄家赢的
            $xianMoney = bcsub(0, $tempMon, 2); // 闲家输的
        } else {
            // 庄输；庄家失败所输 ＝ A*N*X*Y | 闲家胜利所赢 ＝ A*N*X*Y －抽水
            $tempMon = $fjmoney * $xjBets; // $xjCardMut * $zjMutle
            $mainMoney = bcsub(0, $tempMon, 2); // 闲家赢的
            $xianMoney = bcsub($tempMon, 0, 2); // 庄家输的
        }
        return $mainMoney;
    }

    /**
     * TODO 异步监控房间内下注
     * asycStartUserBetMuts
     */
    protected function StartUserBetMuts($gameId, $gameRId, $gameNo, $players = false, $zjUserid = 0, $zjUserInfo = false)
    {
        BaseEnum::Logger('闲家异步统计：PlayTwoEight-(StartUserBetMuts)-统计 ---->>>>>>>>>>>>> 待完善:' . $gameNo, 'user-login');
        // 8秒动画效果；
        $Long = $this->getConfigTimeLong('waitbet_long') ?? 8;
        Timer::after($Long * 1000, function () use ($gameNo, $gameId) {
            $fjDetail = BaseComm::distMemoryRooms($gameNo, FALSE); // 房间内的信息；
                                                               // 查看当前状态是否启动；
            if (isset($fjDetail["doNext"]) && intval($fjDetail["doNext"]) > 0) { // 已经处理过了；
                BaseEnum::Logger('异步自动下注执行：PlayTwoEight-(StartUserBetMuts)-[检查到doNext=1 已经执行处理过，不再处理]--->>>' . json_encode($fjDetail), 'user-login');
            } else {
                $fjDetail["doNext"] = 1;
                BaseComm::distMemoryRooms($gameNo,TRUE,$fjDetail); // 保存标识不允许再次发起
                $this->doNextPlayBets(null, $gameNo, $fjDetail, 0);
            }
        });
    }
    
    //TODO 生成色子推送出来
    protected function startRandDott($room_No,$playerFds=FALSE){
        $getAlldata = BaseComm::distMemoryRooms($room_No, FALSE);
        $gameJu = isset($getAlldata['currInning']) ? $getAlldata['currInning'] : 1; // 当前游局号
        $num1 = rand(1, 6);                 //摇甩子号码随机处理
        $num2 = rand(1, 6);
        $searray = array($num1,$num2);      // 摇色子号码
        $seSum = array_sum($searray);
        $firstStamp = isset($getAlldata["create"]) ? intval($getAlldata["create"]) : 0;
        $long = BaseEnum::getNowTimeLong($firstStamp);
        BaseComm::gameLogger($room_No,"开局第{$long}秒 , 摇色子点数");
        
        
        //TODO  摇色子
        $sendDott = array( 'm' => BaseEnum::XYM_0X30, "data" =>   ["current" => $gameJu,"nums" => $searray, "dottod" => $seSum]  );
        AuthCommon::pushFrontWeb($playerFds, json_encode($sendDott)); 
    }
    

    /**
     * TODO 开始发牌操作；
     *
     * @param array $paramList
     */
    protected function StartCards($gameId, $gameRId, $gameNo, $playerFds = false)
    {
        //TODO  摇色子
        $this->startRandDott($gameNo,$playerFds);
        
        //TODO  暂停3秒发牌
        $Long = $this->getConfigTimeLong('waitcard_long') ?? 2;
        Timer::after($Long * 1000, function () use ($gameId, $gameRId, $gameNo, $playerFds) {
            $useCards = BaseComm::distMemoryCards($gameNo,FALSE); //数据：[[4,10],[8,4],[4,8],[7,1]]]
            $gameDetails = BaseComm::distMemoryRooms($gameNo,FALSE);
            $firstStamp = isset($gameDetails["create"]) ? intval($gameDetails["create"]) : 0;
            BaseEnum::Logger('取内存数据：PlayTwoEight-(BaseDB::gameRoomPlayerData)-[asycStartCards]--->>>' . json_encode($gameDetails), 'user-login');
            
            $userAllCards = [];
            if (!empty($useCards)) {
                foreach ($useCards as $vs => $onecard){
                    $userAllCards[$vs] = ['seatNo'=> $vs,"cards" =>$onecard];
                }
            }
            if (empty($userAllCards)) {
                BaseEnum::Logger("重新启动发牌：PlayTwoEight-(asycStartCards)-；===={$gameRId}==>{$gameNo}| XYM_0X19 --> ==allUserFds : " . json_encode($playerFds), 'user-login');
                $useCards = GameCommon::createCardList($gameId, $gameNo, false);
            }
            
            $long = BaseEnum::getNowTimeLong($firstStamp);
            BaseComm::gameLogger($gameNo,"开局第{$long}秒 , 发牌");
            
            
            BaseEnum::Logger("发牌推送：PlayTwoEight-(asycStartCards)-；===={$gameRId}==>{$gameNo}| XYM_0X19 --> ==data : " . json_encode($useCards), 'user-login');
            
            // 当前游局号
            $gameJu = isset($gameDetails['currInning']) ? $gameDetails['currInning'] : 1;
            $playerCards = [];
            if (isset($userAllCards) && ! empty($userAllCards)) {
                
                //TODO 修改为发牌成功
                $gameDetails['fjStatus'] = CardEnum::GAME_STEP_3; 
                if (isset($gameDetails['lists']) && ! empty($gameDetails['lists'])) {
                    foreach ($gameDetails['lists'] as $setiid => $fg) {
                        $gameDetails['lists'][$setiid]["pstatus"] = CardEnum::GAME_STEP_3;
                    }
                }

                //TODO 分发给每个用户(分配发牌 )
                foreach ($userAllCards as $pushOneMsg) {
                    $seatId = isset($pushOneMsg['seatNo']) ? intval($pushOneMsg['seatNo']) : 0;
                    $onecards = $pushOneMsg['cards'] ?? FALSE;
                    if ($seatId !== false && $seatId >= 0 && ! empty($onecards)) {
                        $playerCards[$seatId] = [ "seatNo" => $seatId, "cards" => $onecards, "current" => $gameJu, "step" => 1 ];
                    }
                }
                // 保存用户数据
                BaseComm::distMemoryRooms($gameNo,TRUE,$gameDetails); 
                BaseEnum::wirteRedisData(SysEnum::ONLINE_ROOMS_LIST_KEY . $gameNo, json_encode($gameDetails)); // 缓存；
                
                // 启动牌值计算数据；====> 看牌过程
                $this->asycWaitDealCenter($gameRId, $gameNo, $playerCards, $playerFds);
            } else {
                BaseEnum::Logger('发牌失败：PlayTwoEight-(asycStartCards)-发牌；======>   失败 Fail !! -->  ' . $gameNo, 'user-login');
            }
        });
    }

    /**
     * TODO 看牌结算处理；
     *
     * @param string $room_No
     * @param array $useCards
     * @param boolean $playerFds
     */
    protected function asycWaitDealCenter($gameRId, $gameNo, $useCards, $playerFds = FALSE)
    {
        $Long = $this->getConfigTimeLong('waitdeal_long') ?? 5;
        Timer::after($Long * 1000, function () use ($gameRId, $useCards, $gameNo) {
            $allCards = [];
            $allUserIds = [];

            // 房间信息
            $roomDetail = BaseComm::GameRoomDetail($gameRId);
            
            // 棋牌内容：
            $fjDetail = BaseComm::distMemoryRooms($gameNo, FALSE);
            $currentInning = isset($fjDetail['currInning']) ? intval($fjDetail['currInning']) : 0;  // 当前局
            $allInning = isset($fjDetail['playNumber']) ? intval($fjDetail['playNumber']) : 1;      // 玩的局数
            $firstStamp = isset($fjDetail["create"]) ? intval($fjDetail["create"]) : 0;
            $countStamp = BaseEnum::getNowTimeLong($firstStamp);
            $gameLoggs= ["开局第{$countStamp}秒 , 结算"];
            
            
            $doBets = $fjDetail['bets'] ?? FALSE;
            $userbetList = false;
            if (!empty($fjDetail) && isset($fjDetail['lists'])) {
                $userbetList = $fjDetail['lists'];
                $playerFds = ! empty($userbetList) ? array_column($userbetList, "fd", "seatNo") : false;    // FD防变动
                $allUserIds = ! empty($userbetList) ? array_column($userbetList, "uid") : false;            // UID
            }
            BaseEnum::Logger("二八杠结算处理：PlayTwoEight-(asycWaitDealCenter)-[roomid={$gameRId}]；======> Start ....-->UserList=" . json_encode($userbetList), 'user-login');
            BaseEnum::Logger("二八杠结算：PlayTwoEight-(asycWaitDealCenter)-[roomid={$gameRId}]；======> getRoomInfo=" . json_encode($roomDetail), 'user-login');

            // 座位号
            $uSeatList = ! empty($userbetList) ? array_column($userbetList, null, 'seatNo') : false;

            // 比牌数据操作；$useCards
            $zjObject = false;
            $zjUserId = 0;
            $lastNextGameStart = TRUE; 
            $minRoomFee = isset($roomDetail["smallPoints"]) ? intval($roomDetail["smallPoints"]) : 1;       // 最少进入房间底注
            if (! empty($useCards) && ! empty($uSeatList) && ! empty($userbetList)) {
                foreach ($useCards as $seatId => $cardinfo) {
                    $allCards[$seatId] = isset($cardinfo["cards"]) ? $cardinfo["cards"] : false;                    // 取牌数据
                    $useFo = isset($uSeatList[$seatId]) ? $uSeatList[$seatId] : false;
                    $oneUid = isset($useFo['uid']) ? intval($useFo['uid']) : 0;
                    $useCards[$seatId]["userId"] = $oneUid;
                    $useCards[$seatId]["mainFlag"] = isset($useFo['mainflag']) ? intval($useFo['mainflag']) : 0;    // 庄家标识
                    $useCards[$seatId]["mainMutle"] = isset($useFo['mutdotts']) ? intval($useFo['mutdotts']) : 1;   // 下注或抢庄的倍数关系
                    if ($useCards[$seatId]["mainFlag"] === 1) {
                        $zjObject = $useCards[$seatId];
                        $zjUserId = $oneUid;
                        unset($useCards[$seatId]); // 庄家牌内容
                    }
                }

                //TODO  结算处理；
                BaseEnum::Logger('二八杠庄家：PlayTwoEight-(asycWaitDealCenter)===-->' . json_encode($zjObject), 'user-login');
                BaseEnum::Logger('二八杠闲家：PlayTwoEight-(asycWaitDealCenter)===-->' . json_encode($useCards), 'user-login');
                
                $realUserNotices = []; //真实玩家的结算数据
                
                //TODO 结算玩家的计算；把庄家跟闲家进行一一对比；
                $resultList = GameCommon::dealPlayMoney($gameRId, $gameNo, $zjObject, $useCards, $roomDetail,$realUserNotices); // 结算内容

                //TODO 推送：所有牌数据和输赢；
                if (! empty($resultList) && sizeof($resultList) > 1) {
                    BaseEnum::Logger('二八杠结算处理：PlayTwoEight-(asycWaitDealCenter)======> SUCCESS ....-->  ' . json_encode($resultList), 'user-login');
                    $fjDetail['fjStatus'] = CardEnum::GAME_STEP_5;
                    if (isset($fjDetail['lists']) && ! empty($fjDetail['lists'])) {
                        foreach ($fjDetail['lists'] as $setiid => $fg) {
                            $fjDetail['lists'][$setiid]["pstatus"] = CardEnum::GAME_STEP_5;
                        }
                    }
                    BaseComm::distMemoryRooms($gameNo,TRUE,$fjDetail);      // 保存用户数据
                    BaseEnum::wirteRedisData(SysEnum::ONLINE_ROOMS_LIST_KEY . $gameNo, json_encode($fjDetail)); // 缓存；
                    foreach ($resultList as $setid => $res) {
                        $oneuid = $res['uid'] ?? 0;
                        $cellfee = $res['cellScore'] ??0;
                        $chagefee = $res['changeMoney'] ?? 0;
                        $one_betnum = $doBets[$setid] ?? 0;
                        $cards2 = isset($allCards[$setid]) ? $allCards[$setid] : [];   // 取牌数据
                        $set_points = isset($res['points']) ? floatval($res['points']) : 0;
                        if (intval($oneuid) == $zjUserId)  $one_betnum = !empty($doBets) ? array_sum($doBets) : 1;                      //TODO　庄家下注倍数为所有下注倍数之和
                        //TODO 判断points是否可以继续下一局游戏
                        if ($minRoomFee >0 && $set_points < $minRoomFee) {
                            $lastNextGameStart = FALSE;
                        }
                        $resultList[$setid]['lastBetMutle'] = $one_betnum;                                                              //TODO　　玩家最后的下注倍数
                        $gameLoggs[] = "{$setid} 号位  本局输赢:{$chagefee},下注分：{$cellfee},牌值：".json_encode($cards2);                //TODO　　对局日志记录
                        if (!isset($res["cards"]) || empty($res["cards"])) {
                            $resultList[$setid]["cards"] = $cards2;             // 取牌数据
                        }
                    }
                    
                    $lastDeals = array_values($resultList);                     //最后的结算数据；
                    if(!empty($lastDeals) && sizeof($lastDeals) < 4){           //不足的情况下，加上虚拟人员
                        $Card2 = PlayHelp::getInstance()->getRoomCards($gameNo);
                        for ($set2 = sizeof($lastDeals); $set2 < 4; $set2 ++) {
                            $dealval  = ["seatNo"=> $set2 ,"card_mutle"=> 1 ,"uid"=>-1,"level"=> -1,"lastBetMutle"=> 0];
                            $dealval["cards"] = isset($Card2[$set2]) ? $Card2[$set2] : []; // 取牌数据
                            if (isset($dealval["cards"]) && !empty($dealval["cards"])) {
                                MahjEnum::dealTwoEightLevel($dealval["cards"], $dealval["level"]); //计算牌型
                            }
                            $lastDeals[] = $dealval;    //牌型数据就不要
                        }
                    }
                    BaseComm::distLastGameNoListRedis($allUserIds,TRUE,FALSE);      //清掉最新局号

                    
                    //TODO 推送已出的牌的数据内容
                    $CommgameNo = substr("{$gameNo}", -10); 
                    $alreadyCards = $this->getAlreadyCards($CommgameNo);
                    $pushdata2 = [ 'm' => BaseEnum::XYM_0X25, "data" =>  $alreadyCards];
                    AuthCommon::pushFrontWeb($playerFds, json_encode($pushdata2));                               // 推送不同的数据；
                    
                    // 开启下一局：30秒后再统计；
                    $minDiMoney = isset($roomDetail["everyPoints"]) ? intval($roomDetail["everyPoints"]) : 1;   // 底注钱
                    $gameId = isset($fjDetail['gameId']) ? intval($fjDetail['gameId']) :0 ;
                    
                    
                    $commonStop = ($allInning > 0 && $currentInning > 0 && $currentInning < $allInning ) ? TRUE : FALSE ;
                    //TODO 是否要重新开局；复制当前所有玩家信息，重新开局
                    if ($allInning > 0 && $currentInning > 0 && $currentInning < $allInning ) {
                        $commonStop = TRUE;
                    }
                    BaseEnum::Logger("检测是否下一局：总共{$allInning}局，已完成{$currentInning}局,  gameId = {$gameId},players=" . print_r($playerFds, true), 'user-login');
                    BaseComm::gameLogger($gameNo,$gameLoggs,["finish"=>TRUE]);                  //TODO 加入对局日志
                    
                    
                    //TODO 检查真实玩家用户掉线判断问题(如有玩家掉线 强制游戏结束 ====> GAME OVER )
                    $gamePdata = BaseComm::distMemoryRooms($gameNo, FALSE);
                    $userList = $gamePdata['lists'] ?? FALSE;
                    $gameIsStop = PlayCommon::gameUserIsFinish($gameId,$userList);
                    BaseEnum::Logger("[房间：{$gameRId}]下一局掉线检查： ( 是否强行结束 - isStop : {$gameIsStop}) -- 玩家：" . json_encode($userList), 'user-login');
                    
                    
                    if($lastNextGameStart === FALSE || $gameIsStop === TRUE ){
                        BaseEnum::Logger("二八所有结算提前完成：所有轮游戏提前结束！！！！lastNextGameStart = {$lastNextGameStart},gameOutStop ={$gameIsStop} ；==>  ". print_r($lastDeals, true), 'user-login');
                        $pushdata = ['m'=>BaseEnum::XYM_0X1E,"data"=>array("gameNo"=>$gameNo,"gameId"=>$gameId,"gameIsStop"=>$gameIsStop),"finish"=> TRUE ,"msg"=> "游戏结束。"];
                        AuthCommon::pushFrontWeb($playerFds, json_encode($pushdata));     
                    }
                    
                    //TODO 前端结算：
                    Timer::after(500, function() use($playerFds,$lastDeals) {
                        $pushdata = [ 'm' => BaseEnum::XYM_0X1C, "data" => $lastDeals];
                        AuthCommon::pushFrontWeb($playerFds, json_encode($pushdata));                               // 推送不同的数据；
                    });
                    
                    //TODO 检查所有玩家提前结束游戏( 没有达到游戏房间的底注要求 )
                    if($lastNextGameStart === FALSE || $commonStop === FALSE || $gameIsStop === TRUE){
                        BaseComm::changeOnLines($gameId ,sizeof($allUserIds) ,1);               //TODO 在线玩家数据（最后一局游戏结束，在线人数减少）
                        PlayCommon::doBackFreeRobot($gameId,$allUserIds);                       //TODO 清掉所有机器人玩家被占用的缓存
                        PlayCommon::gameNoticeSaveCache($gameId,$gameNo,$realUserNotices);      //TODO 加入游戏通知列表
                        return FALSE; 
                    }else{
                        $Long = $this->getConfigTimeLong('nextgame_long') ?? 30;
                        Timer::after($Long * 1000, function () use ($gameId,$gameRId, $gameNo, $fjDetail, $minDiMoney,$allUserIds) {
                            $currentInning = isset($fjDetail['currInning']) ? intval($fjDetail['currInning']) : 0;      // 当前局
                            $usrList = isset($fjDetail['lists']) ? $fjDetail['lists'] : FALSE;
                            
                            //TODO 是否要重新开局；复制当前所有玩家信息，重新开局
                            if(!empty($usrList)){
                                //TODO  用户数据要清空值
                                foreach ($usrList as & $user) {
                                    if (isset($user["uid"]) && intval($user["uid"]) > 0) {
                                        $user['pstatus'] = 0;
                                        $user['mainflag'] = 0;
                                        $user['mutdotts'] = 0;
                                        $user_points = AuthCommon::getLoginUserPoints($user["uid"]);
                                        $countMaxMut = (floatval($user_points) > $minDiMoney) ? intval($user_points / $minDiMoney) : 1; // 抢庄倍数： 抢庄玩家本局能承受的最大赔付金额是低注的多少倍数。
                                        $user['points'] = $user_points;                                                                 // 玩家金额 > 房主的限额；
                                        $user['maxQzMutle'] = (isset($countMaxMut) && $countMaxMut > 200) ? 200 : $countMaxMut;         // 最小倍数
                                        $user['betmoney'] = 0;
                                    }
                                }
                                $this->ReStartNewGame($gameRId, $gameNo, $usrList, $currentInning);                                     //TODO 重新启动第二局游戏
                            }
                        });
                    }
                } else {
                    BaseEnum::Logger('二八杠结算处理：结算失败 ！！！！PlayTwoEight-(asycWaitDealCenter)-；======>  ' . print_r($zjObject, true), 'user-login');
                    return false;
                }
            }
        });
    }
    
    //TODO 未开牌数据
    private function getAlreadyCards($CommgameNo){
        $alreadyCards = BaseComm::distMemoryCards($CommgameNo,FALSE);                               //房间数据
        $rcards = !empty($alreadyCards['cards_nums']) ? $alreadyCards['cards_nums'] : FALSE;
        $notCards = [];
        for ($i = 1; $i <= 10; $i++) {
            $pkey = 'num_'.$i;
            $oneNum = isset($rcards[$pkey]) ? intval($rcards[$pkey]) :0 ;   //解决数据
            $notCards[] = ["key"=> $i,"num"=> $oneNum];
        }
        return $notCards;
    }

    /**
     * TODO 重启游戏；
     * TODO 取回原来有效的数据； 生成新的房间信息 ，复制玩家信息 重新开局； 
     *
     * @param int $gameRId
     * @param string $gameNo
     * @param boolean $usrList
     * @param boolean $roomDetail
     */
    protected function ReStartNewGame($gameRId, $gameNo, $usrList = FALSE, $currentInning = 1) {
        $currentInning = ($currentInning < 1) ? 1 : $currentInning;
        $commSid = substr("{$gameNo}", - 10);       // 取末尾的10位；
        $currentJu = intval($currentInning) + 1;    // 本场局次：
        $lastChar = "{$currentInning}{$commSid}";

        // 房间信息
        $fjInfo = BaseComm::GameRoomDetail($gameRId);;
        $gameId = isset($fjInfo['gameId']) ? intval($fjInfo['gameId']) : 0; // 多少局结束
        $playNum = isset($fjInfo['playNumber']) ? intval($fjInfo['playNumber']) : 1; // 多少局结束

        // 生成新的游戏号：
        $NewgameNo = BaseComm::getRoomUnioxNo($gameRId, $lastChar);
        $fjInfo["users_num"] = sizeof($usrList);
        $fjInfo["users"] = $usrList; // 游戏数据
        $allRecUids = !empty($usrList) ? array_unique(array_column($usrList, "agentId")) : FALSE;
        $fjInfo["agentsIds"] = $allRecUids ?? FALSE ;  
        $dbEx = GameCommon::createRoomsPlayinfo($gameRId, $NewgameNo, $fjInfo, 0);      // 房间落地写入DB并开始发牌
        BaseEnum::Logger('继续下一局 ！PlayTwoEight-(ReStartNewGame)-；======>  ' . print_r([ $gameRId,  $gameNo, $NewgameNo, $dbEx ], true), 'user-login');
        if(!$dbEx){ 
            //游戏启动失败；
            BaseEnum::Logger("组建游戏新桌号：(createRoomsPlayinfo)-=={$NewgameNo} -- >>>重新启动游戏失败!!!" . json_encode($usrList), 'user-login');
            return FALSE;
        }
        
        //TODO 组装房间的状态信息；和玩家数据(fjStatus: 0 == 房间抢庄 )
        $fjPlays = array(
            "isOpen" => 1,
            "gameId" => $gameId,
            'grid' => $gameRId,
            "gameNo" => $NewgameNo,
            "fjStatus" => CardEnum::GAME_STEP_0,
            "currInning" => $currentJu,
            "playNumber" => $playNum,   // 最多玩多少局才能结束；
            "create" => time(),         // 创建时间
            "lists" => $usrList
        );
        BaseComm::distMemoryRooms($NewgameNo,TRUE,$fjPlays);     // 保存数据到内存
        BaseComm::distMemoryRooms($gameNo, TRUE,FALSE);          // 清空内存数据
        BaseComm::gameLogger($NewgameNo,"第{$currentJu}局正式开始");
        
        //TODO 缓存玩家最近的游戏号；
        $allUids = array_column($usrList, "uid");
        BaseComm::distLastGameNoListRedis($allUids,TRUE,json_encode([$NewgameNo,$gameRId]));        //缓存玩家最近的房间号；
        //$redisobj->setRedisValue(SysEnum::ONLINE_ROOMS_LIST_KEY . $NewgameNo, json_encode($fjPlays), 7200); // 缓存游戏信息；
        BaseEnum::Logger("组建游戏新桌号：(joinGameRidUsers)-=={$NewgameNo} -- >>>" . json_encode($fjPlays), 'user-login');
        $gameResult = [
            "gstart" => 1,
            "gameNo" => $NewgameNo,
            "gameInning" => $currentJu,
            "isFirst" => 0,
            "roomInfo"=> ["curInning"=>$currentJu,"gameId"=> $gameId,"total"=>5]
        ];
        //别外信息
        //$roomLastPots = isset($fjInfo["smallPoints"]) ? floatval($fjInfo["smallPoints"]) : 0;   // 房间的携带分要求；
        //$roomEveryPots = isset($fjInfo["everyPoints"]) ? floatval($fjInfo["everyPoints"]) : 0;  // 房间底分
        //$gameResult["roomInfo"] = array("gameId"=>$gameId,"everyPoints"=>$roomEveryPots,"smallPoints"=>$roomLastPots,"curInning"=> $currentJu,"total"=>$playNum); //默认为第一局
        $players_fd = array_column($usrList, "fd", "seatNo");
        $push_data = [
            'm' => BaseEnum::XYM_0X16,
            "data" => $gameResult
        ];
        AuthCommon::pushFrontWeb($players_fd, json_encode($push_data)); // 推送数据到前端；游戏开始；
        
        //TODO 互动开始
        $this->asycStartGame($NewgameNo, $gameRId, $fjInfo);
    }
}

?>